#include "libcomm.h"
#include <sstream>
#ifdef COMM_MSVC
#include <DbgHelp.h>
#pragma comment(lib,"DbgHelp.Lib")
#elif !defined(__CYGWIN__)
#include <execinfo.h>
#endif

String StackTracer::TraceStack()
{
#ifdef COMM_MSVC
	{
		std::ostringstream oss;
		void* pStack[COMM_STACKTRACER_MAX_FRAMES];
		HANDLE process = GetCurrentProcess();
		SymInitialize(process, NULL, TRUE);
		WORD frames = CaptureStackBackTrace(0, COMM_STACKTRACER_MAX_FRAMES, pStack, NULL);

		oss << "stack traceback: " << std::endl;
		for (WORD i = 0; i < frames; ++i) 
		{
			DWORD64 address = (DWORD64)(pStack[i]);

			DWORD64 displacementSym = 0;
			char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
			PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
			pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
			pSymbol->MaxNameLen = MAX_SYM_NAME;

			DWORD displacementLine = 0;
			IMAGEHLP_LINE64 line;
			//SymSetOptions(SYMOPT_LOAD_LINES);
			line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);

			if (SymFromAddr(process, address, &displacementSym, pSymbol)
				&& SymGetLineFromAddr64(process, address, &displacementLine, &line)) 
			{
				oss << "\t\"" << pSymbol->Name << "\" at " << line.FileName << ":" << line.LineNumber << "(0x" << std::hex << pSymbol->Address << std::dec << ")" << std::endl;
			}
			else 
			{
				oss << "\tresolve address(0x" << std::hex << address << std::dec << ") error: " << GetLastError() << std::endl;
			}
		}
		return oss.str().c_str();
	}
#elif defined(__CYGWIN__)
	{
		#warning "!!!back-trace was not supported in cygwin version!!!"
		return String::Empty;
	}
#else
	{
		std::ostringstream oss;
		void* pStack[COMM_STACKTRACER_MAX_FRAMES];
		char** pStackList = NULL;
		int frames = 0;
		frames = backtrace(pStack, COMM_STACKTRACER_MAX_FRAMES);
		pStackList = backtrace_symbols(pStack, frames);

		oss << "stack traceback: " << std::endl;
		if (NULL != pStackList)
		{
			for (int i = 0; i < frames; ++i)
			{
				oss << "\t" << pStackList[i] << std::endl;
			}

			free(pStackList);
		}
		return oss.str().c_str();
	}
#endif
}
